package com.sangeng.config;


import com.sangeng.filter.JwtAuthenticationTokenFilter;
import com.sangeng.handler.AccessDeniedHandlerImpl;
import com.sangeng.handler.AuthenticationEntryPointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;

/**
 * @author LENOVO
 * @version 1.0
 * @date 2025/6/17 15:09
 * @deprecated 密码编码器配置修改
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启全局方法权限控制(用注解定义对应资源的权限)
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    /**
     * 密码编码器
     * <p>
     * 原本默认的密码编码器要求数据库中存储密码的字段格式为 {id} + 密码
     * 表示根据id 选择对应的密码编码器进行密码编码 比如 {bcrypt} + 密码 表示密码部分需要使用BCrypt进行编码 用于标识使用的加密算法。
     *
     * 此处我们自定义使用 BCryptPasswordEncoder密码编码器，表示所有密码都将使用 bcrypt 算法进行加密，
     * 但是数据库中存储的密码无需添加 {bcrypt} 前缀。
     *
     * @return 密码编码器 BCryptPasswordEncoder是 PasswordEncoder的实现类
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();//使用 bcrypt 强哈希算法对密码进行加密
    }




    /**
     * 配置用户信息服务
     *
     * 在本项目中我们用数据库来存储用户信息,这里只是学习springSecurity默认的获取用户信息的方式,就是去内存中存储用户信息
     * 这个InMemoryUserDetailsManager确实是UserDetailsService接口的实现类,
     * 后续登录认证请求打到这里,会在InMemoryUserDetailsManager中根据用户名去内存中获取用户信息
     * 而本项目我们没用这个,而是自定义了一个别的实现类UserDetailsServiceImpl实现根据用户名去数据库获取用户信息
     *
     * @return
     */
    /*@Bean
    public UserDetailsService userDetailsService() {
        //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());//创建用户zhangsan,密码123,权限为p1
        manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
        return manager;
    }
*/

    /**
     * 获取AuthenticationManager对象
     *
     * @return AuthenticationManager
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    @Autowired
    private  JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    private AuthenticationEntryPointImpl authenticationEntryPoint;

    @Autowired
    private AccessDeniedHandlerImpl accessDeniedHandler;

    /**
     * 配置SpringSecurity放行的请求
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭csrf(这里返回的对象是HttpSecurity http,所以后面可以链式)
                .csrf().disable()

                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//设置为无状态会话策略，表示不创建或使用HTTP Session来保存安全上下文。
                .and()//结束当前的配置块，并返回上一层配置对象,比如当前配置块的结束，返回上一层配置.csrf().disable()的对象HttpSecurity http,是的后续还能继续链式配置

                .authorizeRequests()//开始配置请求的访问权限控制。也就是下面这些
                // 对于登录接口 允许匿名访问 (匿名访问是指未经过身份验证的用户也可以访问特定的接口或资源。但是已经认证的用户不能访问)
                .antMatchers("/user/login").anonymous()//意味着没有token也能访问/user/login,但是有token就不能访问了
                //.antMatchers("/user/hello").permitAll()//允许所有用户访问/user/hello,无论登录与否都可以访问

                .antMatchers("/testCors").hasAnyAuthority("system:dept:test")//基于配置实现的权限控制,配置权限控制,
                // 这里配置了/testCors接口的权限控制,只有有system:dept:test权限的用户才能访问,替代了使用@PreAuthorize("hasAuthority('system:dept:test')")
                //注意如果同时使用@PreAuthorize("hasAuthority('system:dept:test')")和配置权限控制,则只有 配置权限控制 生效

                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();

        // 将我们自定义的过滤器jwtAuthenticationTokenFilter添加到过滤器链中,参数1是要添加的过滤器，参数2是要添加的位置再那个过滤器之前
        //这里类似拦截器的添加自定义拦截器部分,也要设置添加的位置
        http.addFilterBefore(jwtAuthenticationTokenFilter , UsernamePasswordAuthenticationFilter.class);

        // 配置异常处理器
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)//认证失败处理器配置
                .accessDeniedHandler(accessDeniedHandler);//授权失败处理器配置

        //允许跨域
        http.cors();

    }



}